<?php

namespace App\Kuafu\Wechat\Service;

use App\Kuafu\Activity\Enum\ActivityEnum;
use App\Kuafu\Activity\Service\ActivityService;
use App\Kuafu\Common\Controller\B\UploadController;
use App\Kuafu\Config\Service\ConfigService;
use App\Kuafu\Customer\Enum\PlatformCodeEnum;
use App\Kuafu\Customer\Model\CustomerPlatformModel;
use App\Kuafu\Customer\Service\CustomerPlatformService;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Service\ShopAppletService;
use App\Kuafu\Shop\Service\ShopService;
use App\Kuafu\Wechat\Enum\CodeEnum;
use App\Kuafu\Wechat\Enum\WechatCodeEnum;
use App\Kuafu\Wechat\Exceptions\WechatException;
use App\Kuafu\Wechat\Service\Event\WechatEvent;
use App\Kuafu\Wechat\Service\Message\WechatMessage;
use App\Support\AppletEncrypt;
use CURLFile;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use App\Kuafu\Base\Service\BaseService;
use GuzzleHttp\Client as GuzzleClient;
use League\CommonMark\Util\UrlEncoder;

class WechatCallbackService extends BaseService
{
    const TOKEN = "weixin";

    public static function valid(Request $request)
    {
        Log::channel('wechat')->info("微信公众平台校验进入");
        $echoStr = $request->get('echostr');
        if (self::checkSignature($request)) {
            echo $echoStr;
            exit();
        }
    }

    private static function checkSignature(Request $request): bool
    {
        if (!self::TOKEN) {
            throw new WechatException(WechatCodeEnum::ERR_NO_TOKEN);
        }
        $signature = $request->get('signature');
        $timestamp = $request->get('timestamp');
        $nonce     = $request->get('nonce');
        $token     = self::TOKEN;
        $tmpArr    = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);
        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 接收微信事件消息
     */
    public static function responseMsg()
    {
        Log::channel('wechat')->info('接收事件消息:进入');
        $postStr = file_get_contents('php://input'); //获得用户发送信息
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
        $postObj = json_encode($postObj);
        $postObj = json_decode($postObj, true);
        Log::channel('wechat')->info('接收事件消息：' . json_encode($postObj));
        switch ($postObj['MsgType']) {
            case 'event':
                Log::channel('wechat')->info('接收事件消息:进入订阅');
                (new WechatEvent($postObj))->handle();
                break;
            case "text" :
                Log::channel('wechat')->info("接收文本消息：进入消息接收");
                (new WechatMessage($postObj))->handle();
                break;
            default:
                exit;
        }
    }


    /**
     * @param $appid
     * @param $secret
     * @param $grantType
     * @return mixed
     * @throws GuzzleException
     */
    public static function getAccessToken()
    {
        $appid  = config('wechat.publicAppid');
        $secret = config('wechat.publicSecret');
        //查询redis缓存
        $cache = Redis::get(CodeEnum::PREFIX_APPLET_CARD_TOKEN . $appid);
        if (!$cache) {
            $grantType = 'client_credential';
            $url       = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" . $grantType . "&appid=" . $appid . "&secret=" . $secret;
            $client    = new GuzzleClient();
            try {
                $response = $client->get($url);
                $result   = $response->getBody()->getContents();
                Log::channel('wechat')->info('微信公众号获取token：' . $result);
                $responseArr = json_decode($result, true);
                if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                    throw new WechatException(WechatCodeEnum::ERR_GET_PUBLIC_TOKEN);
                }
                //设置缓存
                Redis::setex(CodeEnum::PREFIX_APPLET_CARD_TOKEN . $appid, CodeEnum::APPLET_TOKEN_TIME,
                    $responseArr['access_token']);
                return $responseArr;
            } catch (\Exception $exception) {
                throw new WechatException(WechatCodeEnum::ERR_GET_PUBLIC_TOKEN);
            }
        }
        return ['access_token' => $cache];

    }

    /**
     * 获取用户昵称
     * @param $openid
     * @param int $retryCount
     * @return mixed|void
     * @throws GuzzleException
     */
    public static function getUserinfo($openid, int $retryCount = 1)
    {
        $appid  = config('wechat.publicAppid');
        $token = self::getAccessToken();
        $token = $token['access_token'];
        $url   = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $token . '&openid=' . $openid . '&lang=zh_CN';
        try {
            $client   = new GuzzleClient();
            $response = $client->get($url);
            $result   = $response->getBody()->getContents();
            Log::channel('wechat')->info('微信公众号获取token：' . $result);
            $responseArr = json_decode($result, true);
            if (isset($responseArr['errcode']) && $responseArr['errcode'] != 0) {
                Log::channel('wechat')->info('微信公众号获取token失败：',
                    ['result' => $result, 'openid' => $result, 'retryCount' => $retryCount]);

                // 获取失败 重新获取AccessToken
                Redis::del(CodeEnum::PREFIX_APPLET_CARD_TOKEN . $appid);
                if ($retryCount) {
                    return self::getUserinfo($openid, $retryCount - 1);
                }
                throw new WechatException(WechatCodeEnum::ERR_GET_PUBLIC_TOKEN);
            }
            return $responseArr;
        } catch (\Exception $exception) {
            Log::channel('wechat')->info('微信公众号获取token异常：',
                ['msg' => $exception->getMessage(), 'openid' => $result, 'retryCount' => $retryCount]);
            throw new WechatException(WechatCodeEnum::ERR_GET_PUBLIC_TOKEN);
        }
    }


    public static function getQrCode($data, $key)
    {
        //解密代码
        $accessToken = self::getAccessToken();
        $accessToken = $accessToken['access_token'];
        $url         = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" . $accessToken;
        $body        = json_encode($data);
        $data        = [
            'action_name' => 'QR_LIMIT_STR_SCENE',
            'action_info' => [
                'scene' => ['scene_str' => $key . ":" . AppletEncrypt::getInstance()->encryptShort($body, $key)],
            ],
        ];
        $client      = new GuzzleClient();
        $body        = json_encode($data);
        $response    = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
        $response    = $response->getBody()->getContents();
        $responseArr = json_decode($response, true);
        return $responseArr;
    }

    /**
     * 用ticket换取图片
     * @param $ticket
     * @return string
     * @throws GuzzleException
     */
    public static function getImageByTicket($ticket)
    {
        $ticket   = urlencode($ticket);
        $url      = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=$ticket";
        $client   = new GuzzleClient();
        $response = $client->get($url);
        $response = $response->getBody()->getContents();
        return "data:image/jpeg;base64," . base64_encode($response);
    }


    public static function addCardMaterial(Request $request)
    {
        $uploadController = new UploadController($request);
        $jsonResponse     = $uploadController->ImagesUpload($request);
        $data             = $jsonResponse->getData();
        $url              = $data->data->fileUrl;
        return self::uploadMaterial($url);
    }

    public static function uploadOfficialMaterial(string $path, string $filename = '', string $type = 'image')
    {
        $token      = self::getAccessToken();
        $token      = $token['access_token'];
        $upload_url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={$token}&type={$type}";

        $fileInfo = new CURLFile($path);
        if (!$filename) {
            $pathArr  = explode('/', $path);
            $filename = end($pathArr);
        }
        $fileInfo->setPostFilename('rms-api:' . $filename);

        $data['media'] = $fileInfo;
        $json          = self::httpRequestPost($upload_url, $data);
        // json转为数组
        $arr = json_decode($json, true);
        Log::channel('wechat')->info("素材上传结果：" . $arr['media_id']);
        return $arr['media_id'];
    }

    private static function uploadMaterial($path)
    {
        $token      = self::getAccessToken();
        $token      = $token['access_token'];
        $upload_url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" . $token . "&type=thumb";
        $json       = self::httpRequestPost($upload_url, [], $path);

        // json转为数组
        $arr = json_decode($json, true);
        Log::channel('wechat')->info("素材上传结果：" . $arr['media_id']);
        $name  = CodeEnum::APPLET_CARD_NAME;
        $key   = CodeEnum::APPLET_CARD_KEY;
        $value = $arr['media_id'];
        $type  = CodeEnum::APPLET_CARD_TYPE;
        self::addMediaInfoToConfig($name, $key, $value, $type, $remart = '');
        return $arr['media_id'];
    }

    public static function addMediaInfoToConfig($name, $key, $value, $type, $remark)
    {
        ConfigService::addConfig($name, $key, $value, $type, $remark);
    }

    private static function httpRequestPost($url, $data, $file = '')
    {
        if (!empty($file)) {
            $data['media'] = new CURLFile($file);
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        //判断有无出错
        if (curl_errno($curl) > 0) {
            echo curl_error($curl);
            $output = 'http请求出错！' . '[' . curl_error($curl) . ']';
        }
        curl_close($curl);
        return $output;
    }

    public static function createMenu($button)
    {
        //获取access_token
        $token       = self::getAccessToken();
        $token       = $token['access_token'];
        $url         = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=$token";
        $data        = [
            'button' => $button
        ];
        $body        = json_encode($data, JSON_UNESCAPED_UNICODE);
        $client      = new GuzzleClient();
        $response    = $client->request("POST", $url, ['body' => $body, 'timeout' => 10]);
        $response    = $response->getBody()->getContents();
        $responseArr = json_decode($response, true);
        return $responseArr;
    }

    public static function getMenu()
    {
        //获取access_token
        $token = self::getAccessToken();
        return $token;

        $token       = $token['access_token'];
        $url         = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=$token";
        $client      = new GuzzleClient();
        $response    = $client->request("get", $url);
        $response    = $response->getBody()->getContents();
        $responseArr = json_decode($response, true);
        return $responseArr;
    }

    /**
     * 是否测试用户或者测试其他信息
     *
     * @return bool
     */
    private static function isTestShopId($postObj): bool
    {
        try {
            Log::channel('wechat')->info('check_test_shop_postObj_info：',
                $postObj);
            if (in_array($postObj['FromUserName'], [
                'o6tK-55pKJ-X57sJJu14UEaaMVY8', //  夏目
            ])) {

                Log::channel('wechat')->debug('user in debug code', [
                    'openId' => $postObj['FromUserName']
                ]);

                return true;
            }

            return false;

        } catch (\Exception $exception) {
            return false;
        }
    }

}
